/*
 * ControlPanel.java
 *
 * Created on 24 Feb, 2010;
 */

package com.ahmad.fvautoclicker.v2;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.swing.JFrame;

/**
 * 
 * @author Ahmad Nadeem<br>
 *         <a mailto:"ahmad.nk@gmail.com"/>
 */
public class ControlPanel extends javax.swing.JPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private List<Point> positions;
	private List<Marker> markers;

	private Robot robot;

	private JFrame frame;

	public int width = 350;
	public int height = 300;

	/** Creates new form ControlPanel */
	public ControlPanel(JFrame mainFrame) {
		this.frame = mainFrame;
		initComponents();
		try {
			robot = new Robot();
		} catch (AWTException e) {
			e.printStackTrace();
		}
		positions = new ArrayList<Point>();
		markers = new ArrayList<Marker>();
		modeField.setSelected(true);
		rbtnClickMode.add(modePlane);
		rbtnClickMode.add(modeAbsolute);
		rbtnClickMode.add(modeRelative);
		rbtnClickMode.add(modeField);
		showAdvanceSettings(null);

		loadSettings();
	}

	//GEN-BEGIN:initComponents
	// <editor-fold defaultstate="collapsed" desc="Generated Code">
	private void initComponents() {

		rbtnClickMode = new javax.swing.ButtonGroup();
		modeAbsolute = new javax.swing.JRadioButton();
		modeRelative = new javax.swing.JRadioButton();
		modePlane = new javax.swing.JRadioButton();
		modeField = new javax.swing.JRadioButton();
		lblMode = new javax.swing.JLabel();
		lblDelay = new javax.swing.JLabel();
		lblCount = new javax.swing.JLabel();
		lblX = new javax.swing.JLabel();
		lblY = new javax.swing.JLabel();
		scrollPosition = new javax.swing.JScrollPane();
		txtPosition = new javax.swing.JTextArea();
		speed = new javax.swing.JSpinner();
		intervalX = new javax.swing.JSpinner();
		intervalY = new javax.swing.JSpinner();
		fieldsX = new javax.swing.JSpinner();
		fieldsY = new javax.swing.JSpinner();
		btnLocate = new javax.swing.JButton();
		btnClear = new javax.swing.JButton();
		btnTest = new javax.swing.JButton();
		btnRun = new javax.swing.JButton();
		btnAdvance = new javax.swing.JCheckBox();
		btnSave = new javax.swing.JButton();
		lblPointerSize = new javax.swing.JLabel();
		lblPointerMask = new javax.swing.JLabel();
		markerSize = new javax.swing.JSpinner();
		markerMaskX = new javax.swing.JSpinner();
		markerMaskY = new javax.swing.JSpinner();

		setMinimumSize(new java.awt.Dimension(350, 300));
		setLayout(null);

		modeAbsolute.setText("Absolute");
		modeAbsolute.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				setClickModeAbsolute(evt);
			}
		});
		add(modeAbsolute);
		modeAbsolute.setBounds(90, 20, 90, 20);

		modeRelative.setText("Relative");
		modeRelative.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				setClickModeRelative(evt);
			}
		});
		add(modeRelative);
		modeRelative.setBounds(90, 40, 100, 20);

		modePlane.setText("Plane");
		modePlane.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				setClickModePlane(evt);
			}
		});
		add(modePlane);
		modePlane.setBounds(90, 0, 90, 20);

		modeField.setText("Field");
		modeField.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				setClickModeField(evt);
			}
		});
		add(modeField);
		modeField.setBounds(90, 60, 100, 25);

		lblMode.setFont(new java.awt.Font("Sans", 1, 14));
		lblMode.setText("Mode");
		add(lblMode);
		lblMode.setBounds(10, 10, 70, 19);

		lblDelay.setText("Speed");
		add(lblDelay);
		lblDelay.setBounds(10, 130, 60, 16);

		lblCount.setText("Fields");
		add(lblCount);
		lblCount.setBounds(140, 140, 60, 16);

		lblX.setText("x interval");
		add(lblX);
		lblX.setBounds(10, 160, 70, 16);

		lblY.setText("y interval");
		add(lblY);
		lblY.setBounds(10, 190, 70, 16);

		txtPosition.setColumns(20);
		txtPosition.setRows(5);
		scrollPosition.setViewportView(txtPosition);

		add(scrollPosition);
		scrollPosition.setBounds(210, 40, 90, 170);
		add(speed);
		speed.setBounds(90, 130, 40, 20);
		add(intervalX);
		intervalX.setBounds(90, 160, 40, 20);
		add(intervalY);
		intervalY.setBounds(90, 190, 40, 20);

		fieldsX
				.setToolTipText("Nagative value means left side fields, and positive value means right side fields");
		fieldsX.setValue(20);
		add(fieldsX);
		fieldsX.setBounds(140, 160, 40, 20);

		fieldsY
				.setToolTipText("Nagative value means down wards fields, and positive value means upward fields");
		fieldsY.setValue(5);
		add(fieldsY);
		fieldsY.setBounds(140, 190, 40, 20);

		btnLocate.setText("Locate");
		btnLocate.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				getLocation(evt);
			}
		});
		add(btnLocate);
		btnLocate.setBounds(210, 220, 90, 25);

		btnClear.setText("Clear");
		btnClear.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				clearPositions(evt);
			}
		});
		add(btnClear);
		btnClear.setBounds(210, 10, 90, 25);

		btnTest.setText("Test");
		btnTest.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				moveMouse(evt);
			}
		});
		add(btnTest);
		btnTest.setBounds(10, 220, 90, 25);

		btnRun.setText("Run");
		btnRun.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				clickMouse(evt);
			}
		});
		add(btnRun);
		btnRun.setBounds(110, 220, 90, 25);

		btnAdvance.setText("Show Advance Settings");
		btnAdvance.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				showAdvanceSettings(evt);
			}
		});
		add(btnAdvance);
		btnAdvance.setBounds(10, 100, 180, 25);

		btnSave.setText("Save");
		btnSave.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				saveSettings(evt);
			}
		});
		add(btnSave);
		btnSave.setBounds(10, 70, 57, 25);

		lblPointerSize.setText("Pointer Size");
		add(lblPointerSize);
		lblPointerSize.setBounds(10, 270, 80, 16);

		lblPointerMask.setText("Pointer Mask");
		add(lblPointerMask);
		lblPointerMask.setBounds(10, 300, 90, 16);

		markerSize.setValue(4);
		add(markerSize);
		markerSize.setBounds(100, 270, 40, 20);

		markerMaskX.setValue(4);
		add(markerMaskX);
		markerMaskX.setBounds(100, 300, 40, 20);

		markerMaskY.setValue(25);
		add(markerMaskY);
		markerMaskY.setBounds(150, 300, 40, 20);
	}// </editor-fold>
	//GEN-END:initComponents

	private void saveSettings(java.awt.event.ActionEvent evt) {
		Properties p = new Properties();
		String mode = "";
		if (modePlane.isSelected()) {
			mode = "1";
		} else if (modeAbsolute.isSelected()) {
			mode = "2";
		} else if (modeRelative.isSelected()) {
			mode = "3";
		} else if (modeField.isSelected()) {
			mode = "4";
		}

		p.setProperty("mode", mode);
		p.setProperty("speed", speed.getValue().toString());
		p.setProperty("position", txtPosition.getText());
		p.setProperty("intervalX", intervalX.getValue().toString());
		p.setProperty("intervalY", intervalY.getValue().toString());
		p.setProperty("fieldsX", fieldsX.getValue().toString());
		p.setProperty("fieldsY", fieldsY.getValue().toString());
		p.setProperty("markerSize", markerSize.getValue().toString());
		p.setProperty("markerMaskX", markerMaskX.getValue().toString());
		p.setProperty("markerMaskY", markerMaskY.getValue().toString());

		try {
			p.store(new FileOutputStream("fvautoclicker.properties"), null);
		} catch (IOException e) {
		}

	}

	private void loadSettings() {
		File prop = new File("fvautoclicker.properties");
		if (!prop.exists()) {
			saveDefaultSettings();
		}
		Properties p = new Properties();
		try {
			p.load(new FileInputStream("fvautoclicker.properties"));
		} catch (IOException e) {
		}

		if (p.getProperty("mode").equals("4")) {
			modeField.setSelected(true);
		} else if (p.getProperty("mode").equals("3")) {
			modeRelative.setSelected(true);
		} else if (p.getProperty("mode").equals("2")) {
			modeAbsolute.setSelected(true);
		} else if (p.getProperty("mode").equals("1")) {
			modePlane.setSelected(true);
		}

		speed.setValue(Integer.parseInt(p.getProperty("speed")));
		txtPosition.setText(p.getProperty("position"));
		intervalX.setValue(Integer.parseInt(p.getProperty("intervalX")));
		intervalY.setValue(Integer.parseInt(p.getProperty("intervalY")));
		fieldsX.setValue(Integer.parseInt(p.getProperty("fieldsX")));
		fieldsY.setValue(Integer.parseInt(p.getProperty("fieldsY")));
		markerSize.setValue(Integer.parseInt(p.getProperty("markerSize")));
		markerMaskX.setValue(Integer.parseInt(p.getProperty("markerMaskX")));
		markerMaskY.setValue(Integer.parseInt(p.getProperty("markerMaskY")));

	}

	private void saveDefaultSettings() {
		Properties p = new Properties();

		p.setProperty("mode", "4");
		p.setProperty("speed", "50");
		p.setProperty("position", "200,200");
		p.setProperty("intervalX", "25");
		p.setProperty("intervalY", "12");
		p.setProperty("fieldsX", "14");
		p.setProperty("fieldsY", "14");
		p.setProperty("markerSize", "4");
		p.setProperty("markerMaskX", "31");
		p.setProperty("markerMaskY", "7");

		try {
			p.store(new FileOutputStream("fvautoclicker.properties"), null);
		} catch (IOException e) {
		}

	}

	private void showAdvanceSettings(java.awt.event.ActionEvent evt) {
		boolean flag = btnAdvance.isSelected();

		if (flag) {
			height = 350;
		} else {
			height = 300;
		}
		this.setSize(width, height);

		btnSave.setVisible(flag);

		markerMaskX.setVisible(flag);
		markerMaskY.setVisible(flag);
		markerSize.setVisible(flag);
		lblPointerMask.setVisible(flag);
		lblPointerSize.setVisible(flag);

	}

	private void setClickModePlane(ActionEvent evt) {
		txtPosition.setEnabled(false);
		//		txtCount.setEnabled(true);
		fieldsX.setEnabled(false);
		fieldsY.setEnabled(false);
		intervalX.setEnabled(false);
		intervalY.setEnabled(false);
		lblCount.setText("Fields");
	}

	private void setClickModeAbsolute(ActionEvent evt) {
		txtPosition.setEnabled(true);
		//		txtCount.setEnabled(false);
		fieldsX.setEnabled(false);
		fieldsY.setEnabled(false);
		intervalX.setEnabled(false);
		intervalY.setEnabled(false);
		lblCount.setText("Fields");
	}

	private void setClickModeRelative(ActionEvent evt) {
		txtPosition.setEnabled(true);
		//		txtCount.setEnabled(true);
		fieldsX.setEnabled(true);
		fieldsY.setEnabled(false);
		intervalX.setEnabled(true);
		intervalY.setEnabled(true);
		lblCount.setText("Count");
	}

	private void setClickModeField(ActionEvent evt) {
		txtPosition.setEnabled(true);
		//		txtCount.setEnabled(true);
		fieldsX.setEnabled(true);
		fieldsY.setEnabled(true);
		intervalX.setEnabled(true);
		intervalY.setEnabled(true);
		lblCount.setText("Fields");
	}
	
	private boolean isLocating;

	private void getLocation(java.awt.event.ActionEvent evt) {
		isLocating = true;

	}

	public void addPosition(java.awt.event.MouseEvent evt) {
		if (isLocating) {
			Point p = new Point(evt.getXOnScreen(), evt.getYOnScreen());
			markPosition(p);
			// position.add(p);
			if (txtPosition.getText().trim().length() > 0) {
				txtPosition.append("\n" + p.x + "," + p.y);
			} else
				txtPosition.append(p.x + "," + p.y);
		}

		isLocating = false;
	}

	private void markPosition(Point p) {
		int x = p.x;
		int y = p.y;
		showMsg("clicked at " + x, 0);
		Marker m = new Marker();
		m.size = (Integer) markerSize.getValue();

		MainFrame.bg.add(m);
		x -= m.size / 2;
		y -= m.size / 2;

		int maskX = (Integer) markerMaskX.getValue();
		int maskY = (Integer) markerMaskY.getValue();
		x -= maskX;
		y -= maskY;
		/*
		 // -- TODO include getResolutionOfset().
		 // int r = Toolkit.getDefaultToolkit().getScreenResolution();
		 Dimension rs = Toolkit.getDefaultToolkit().getScreenSize();
		 showMsg("system resulation: " + rs, 0);
		 Dimension r1 = new Dimension(1280, 1024);// ahmad nadeem - home
		 Dimension r2 = new Dimension(1680, 1050);// -- ahmad nadeem i2c
		 // Dimension r3 = new Dimension(1024, 768);
		 Dimension r4 = new Dimension(1440, 900);// -- kashif i2c
		 // Dimension r5 = new Dimension(800, 600);
		 //
		 if (rs.equals(r1)) {//-- tested on Win 7
		 x -= 8;
		 y -= 36;
		 } else if (rs.equals(r2)) {// -- tested on fedora, in Win vista it
		 // should be [7,27]
		 x -= 4;
		 y -= 25;
		 }
		 // else if (rs.equals(r3)) {
		 // x -= 4;
		 // y -= 25;
		 // }
		 else if (rs.equals(r4)) {// -- tested on Win XP
		 x -= 3;
		 y -= 29;
		 }
		 // else if (rs.equals(r5)) {
		 // x -= 0;
		 // y -= 0;
		 // }
		 else {
		 // -- READ == for any unknow resulation; trace the exect location
		 // and add in the code;
		 showMsg("Can't mark according to the system resolution:\n"
		 + " now using x and y interval to mark the location", 0);
		 x -= this.x;
		 y -= this.y;
		 }
		
		 */

		markers.add(m);
		m.setBounds(x, y, m.size, m.size);

	}

	private void parseFields() {
		parsePositions();
		Point pt = positions.get(0);

		//-- creating new positions list depending on the first click, and according to YFields.
		positions = new ArrayList<Point>();
		int fields = (Integer) fieldsY.getValue();
		int yx = pt.x;
		int yy = pt.y;
		int X = (Integer) intervalX.getValue();
		int Y = (Integer) intervalY.getValue();
		if (fields > 0) {
			for (int i = 0; i < fields; i++) {
				Point p = new Point(yx, yy);
				showMsg("adding : " + p, 1);
				positions.add(p);
				yx -= X;
				yy -= Y;
			}

		} else {

			for (int i = 0; i < fields; i++) {
				Point p = new Point(yx, yy);
				showMsg("adding : " + p, 1);
				positions.add(p);

				yx += X;
				yy += Y;

			}
		}

		showMsg("total positions added: " + positions.size(), 1);
	}

	private void parsePositions() {
		String pos = txtPosition.getText();
		showMsg(pos, 0);
		if (pos != null) {
			List<String> ls = getStrList(pos.split("\n"));
			// showMsg(ls.size());
			if (ls != null) {
				for (String xy : ls) {
					String[] point = xy.split(",");
					int x = 0, y = 0;
					try {
						x = Integer.parseInt(point[0]);
					} catch (NumberFormatException e) {
						showMsg("Number format exp: setting value to 0", 1);
					}
					try {
						y = Integer.parseInt(point[1]);//-- if we take the
						// point[1], some how it return number format exception
						// may be due to \n, so this is a hotfix for that
						//						String tempY = point[1].substring(1);
						//						showMsg("value of y = " + tempY, 0);
						//						y = Integer.parseInt(tempY);
					} catch (NumberFormatException e) {
						showMsg(
								"Number format exp: setting value to 0 for value ",
								1);
					}
					Point p = new Point(x, y);
					positions.add(p);

				}
			} else {
				showMsg("no position for mouse click, Please define it", 1);
			}

		}

		showMsg("Positions: " + positions, 0);
	}

	private List<String> getStrList(String[] str) {
		List<String> ls = null;
		if (str.length > 0) {
			ls = new ArrayList<String>();
			for (int i = 0; i < str.length; i++) {
				ls.add(str[i]);
			}
		}
		return ls;
	}

	private void clearPositions(ActionEvent evt) {
		positions = new ArrayList<Point>();
		for (Marker m : markers) {
			m.setVisible(false);
		}
		markers = new ArrayList<Marker>();
		txtPosition.setText("");
	}

	private void moveMouse(ActionEvent evt) {
		if (modeField.isSelected()) {
			parseFields();
		} else {
			parsePositions();
		}

		clickMouse(evt, 0);
		positions = new ArrayList<Point>();// -- next time position will again
		// parsed
	}

	private void clickMouse(ActionEvent evt) {
		if (modeField.isSelected()) {
			parseFields();
		} else {
			parsePositions();
		}
		clickMouse(evt, 1);
		positions = new ArrayList<Point>();// -- next time position will again
		// parsed
	}

	private void clickMouse(ActionEvent evt, int button) {

		isRunning = true;
		// frame.setVisible(false);
		frame.setSize(width, height);
		int clickCount = (Integer) fieldsX.getValue();
		int X = (Integer) intervalX.getValue();
		int Y = (Integer) intervalY.getValue();

		if (modePlane.isSelected()) {
			for (int i = 0; i < clickCount; i++) {
				Point p = MouseInfo.getPointerInfo().getLocation();
				clickMouse(button, p);
			}
		} else if (positions.size() == 0) {
			showMsg("position not defined", 1);
			isRunning = false;
			MainFrame.bg.refresh();
			Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
			frame.setSize(d);
			// frame.setVisible(true);
			return;
		}

		if (modeAbsolute.isSelected())
			for (Point p : positions) {
				clickMouse(button, p);
			}
		if (modeRelative.isSelected() || modeField.isSelected()) {
			showMsg("move offset: [ x = " + X + ", y = " + Y + " ]", 0);
			for (Point p : positions) {
				int fields = (Integer) fieldsX.getValue();
				clickCount = fields;
				if (fields > 0) {
					showMsg("moving to next fields row ---------------: ", 1);
					int x = p.x;
					int y = p.y;
					x -= X;
					y += Y;
					for (int i = 0; i < clickCount; i++) {
						x += X;
						y -= Y;
						clickMouse(button, new Point(x, y));
					}
				} else {
					showMsg("moving to next fields row ---------------: ", 1);
					int x = p.x;
					int y = p.y;
					x += X;
					y -= Y;
					for (int i = 0; i < clickCount; i++) {
						x -= X;
						y += Y;
						clickMouse(button, new Point(x, y));
					}
				}
			}
		}
		isRunning = false;
		MainFrame.bg.refresh();
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setSize(d);
		// frame.setVisible(true);
	}

	private boolean stop = false;

	/**
	 * atomic control over click, i.e: click type Left, Right or just move;<br>
	 * can also be used for handling the stop functionality;
	 * 
	 * @param button
	 *            0=do not click, just move mouse; 1=left mouse click; 2=right
	 *            mouse click
	 * @param p
	 *            where to cilck or move the mouse;
	 * */
	private boolean clickMouse(int button, Point p) {
		if (stop) {
			return false;
		}

		switch (button) {
		case 0:
			click(0, p);
			break;
		case 1:
			click(1, p);
			break;
		case 2:
			// TODO not needed yet
			break;

		default:
			return false;
		}

		return false;
	}

	public boolean isRunning = false;

	private void click(int button, Point p) {
		showMsg("moving to " + p, 1);
		robot.mouseMove(p.x, p.y);
		int clickDelay = (Integer) speed.getValue();
		robot.delay(clickDelay);
		if (button == 1) {
			robot.mousePress(InputEvent.BUTTON1_MASK);
			robot.mouseRelease(InputEvent.BUTTON1_MASK);
		}
		if (button == 2) {
			robot.mousePress(InputEvent.BUTTON2_MASK);
			robot.mouseRelease(InputEvent.BUTTON2_MASK);
		}
	}

	//GEN-BEGIN:variables
	// Variables declaration - do not modify
	private javax.swing.JCheckBox btnAdvance;
	private javax.swing.JButton btnClear;
	private javax.swing.JButton btnLocate;
	private javax.swing.JButton btnRun;
	private javax.swing.JButton btnSave;
	private javax.swing.JButton btnTest;
	private javax.swing.JSpinner fieldsX;
	private javax.swing.JSpinner fieldsY;
	private javax.swing.JSpinner intervalX;
	private javax.swing.JSpinner intervalY;
	private javax.swing.JLabel lblCount;
	private javax.swing.JLabel lblDelay;
	private javax.swing.JLabel lblMode;
	private javax.swing.JLabel lblPointerMask;
	private javax.swing.JLabel lblPointerSize;
	private javax.swing.JLabel lblX;
	private javax.swing.JLabel lblY;
	private javax.swing.JSpinner markerMaskX;
	private javax.swing.JSpinner markerMaskY;
	private javax.swing.JSpinner markerSize;
	private javax.swing.JRadioButton modeAbsolute;
	private javax.swing.JRadioButton modeField;
	private javax.swing.JRadioButton modePlane;
	private javax.swing.JRadioButton modeRelative;
	private javax.swing.ButtonGroup rbtnClickMode;
	private javax.swing.JScrollPane scrollPosition;
	private javax.swing.JSpinner speed;
	private javax.swing.JTextArea txtPosition;

	// End of variables declaration//GEN-END:variables

	/**
	 * @param msg
	 * @param msgType
	 *            0 for console debugging, 1 user warn message, 2 user error
	 *            message
	 * */
	private void showMsg(String msg, int msgType) {
		switch (msgType) {
		case 7:// -- dummy
			break;
		default:
			System.out.println(msg);
			break;
		}
	}
}